﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HtmlParser.Dom;

namespace HtmlParser.Html
{
    public class HtmlDocumentEx : GeneralHtmlParser
    {
        private char[] chars = null;
        private int index = 0;
        private XmlDocument doc = new XmlDocument();
        private IList<HtmlElement> nodeList = new List<HtmlElement>();
        private LinkedList<HtmlElement> linkList = new LinkedList<HtmlElement>();
        private char c;
        private int flag;
        private string socHtml = string.Empty;
        public override XmlDocument Parse(string html)
        {
            if (string.IsNullOrEmpty(html))
            {
                return doc;
            }

            socHtml = html;
            return Parse(html.ToCharArray());
        }

        public override XmlDocument Parse(char[] htmls)
        {
            if (htmls == null)
            {
                return doc;
            }

            if (string.IsNullOrEmpty(socHtml) && htmls.Length > 0)
            {
                socHtml = new string(htmls);
            }
            chars = htmls;

            // 注释 脚本 内容 声明 DocType 元素 属性
            // 元素包含属性
            //脚本: <script>开始</script>结束
            //元素: <div>  <开始 >或者 /> 结束
            //文本: >开始 <结束
            //注释: <!-- 开始 -->结束
            //
            ParseDoc();
            return doc;
        }
        public void ParseDoc()
        {
            string name = string.Empty;
            while (!EOF)
            {
                c = GetNextChar();
                switch (c)
                {
                    case '<':
                        #region 元素标记

                        if (Peek(1) == '!')
                        {
                            // 如果是指令
                            c = GetNextChar();
                            flag = Laxer.Specify;
                            break;
                        }
                        else
                        {
                            //是普通元素
                            c = GetNextChar();
                            bool isEnd = false;
                            if (c == '/')
                            {
                                GetNextChar();
                                isEnd = true;
                            }
                            name = GetNodeName();
                            //处理属性
                            var temp = AddNode(name, Laxer.ELEMENT_NODE);
                            ParseTag(temp);
                            if (string.Compare(name, "script", true) == 0)
                            {

                            }
                        }
                        #endregion
                        break;
                    case '>':
                        #region 处理文本
                        #endregion
                        break;
                    case '[':
                        GetNextChar();
                        name = ParseCData();
                        if (Peek() == '[')
                        {
                            GetNextChar();
                            string cdataValue = ParseCDataValue();
                            AddNode(name, cdataValue, Laxer.CDATA_SECTION_NODE);
                            GetNextChar();
                            GetNextChar();
                        }
                        break;
                    case '!':
                        #region 处理指令

                        GetNextChar();
                        name = GetNodeName();
                        if (string.Compare(name, "DOCTYPE", true) == 0)
                        {
                            string value = GetDocTypeValue();
                            AddNode("doctype", value, Laxer.DOCUMENT_TYPE_NODE);
                        }

                        #endregion
                        break;
                    case '-':
                        #region 处理注释
                        if (Peek() == '-' || Peek(1) == '-')
                        {
                            GetNextChar();
                            GetNextChar();
                            flag = Laxer.COMMENT_NODE;
                            string comment = GetComment();
                            AddNode("comment", comment, Laxer.COMMENT_NODE);
                            GetNextChar();
                            GetNextChar();
                        }

                        #endregion
                        break;
                }
            }
        }

        public void ParseTag(HtmlElement parent)
        {
            string attrValue;
            while (!EOF && Peek() != '>' && !(Peek() == '/' && Peek(1) == '>'))
            {
                ProcessWhiteSpace();
                string attrName = ParseAttributeName();
                ProcessWhiteSpace();
                if (Peek() == '=')
                {
                    ProcessWhiteSpace();
                    GetNextChar();
                    c = Peek();
                    if (c == '"' || c == '\'')
                    {
                        GetNextChar();
                        attrValue = ParseAttributeValue(c);
                        GetNextChar();
                    }
                    else
                    {
                        attrValue = ParseAttributeValue2();
                    }
                }
                else
                {
                    attrValue = string.Empty;
                }
                this.AddAttr(parent, attrName, attrValue);
            }// end for while
        }

        public bool MoveToNextTag()
        {
            if (GetNextChar() == '<')
            {
                return true;
            }
            return !EOF;
        }

        #region 添加节点

        private HtmlAttribute AddAttr(HtmlElement parent, string name, string value)
        {
            var ele = new HtmlAttribute(name, doc);
            ele.Value = value;
            ele.HtmlType = Laxer.ATTRIBUTE_NODE;
            parent.Attributes.AddNode(ele);
            return ele;
        }

        private HtmlElement AddNode(string name)
        {
            var ele = new HtmlElement(name, doc);
            nodeList.Add(ele);
            return ele;
        }

        private HtmlElement AddNode(string name, int htmlType)
        {
            var ele = new HtmlElement(name, doc);
            ele.HtmlType = htmlType;
            nodeList.Add(ele);
            return ele;
        }

        private HtmlElement AddNode(string name, string value, int htmlType)
        {
            var ele = new HtmlElement(name, doc);
            ele.Value = value;
            ele.HtmlType = htmlType;
            nodeList.Add(ele);
            return ele;
        }
        #endregion

        #region 操作

        private string GetDocTypeValue()
        {
            int start = this.index;

            while (!EOF && Peek() != '>')
            {
                Move();
            }
            return socHtml.Substring(start, this.index - start);
        }

        private string GetComment()
        {
            int start = this.index;

            while (!EOF && !(Peek() == '-' && Peek(1) == '-' && Peek(2) == '>'))
            {
                Move();
            }
            return socHtml.Substring(start, this.index - start);
        }

        private string GetNodeName()
        {
            int start = this.index;

            while (!EOF && !Char.IsWhiteSpace(Peek()) && Peek() != '>' && !(Peek() == '/' && Peek(1) == '>'))
            {
                Move();
            }
            return socHtml.Substring(start, this.index - start);
        }

        private string ParseAttributeName()
        {
            int start = this.index;
            while (!EOF && !Char.IsWhiteSpace(Peek()) && Peek() != '>'
              && Peek() != '=')
                Move();
            return socHtml.Substring(start, this.index - start);
        }

        private string ParseAttributeValue(char c)
        {
            int start = this.index;
            while (!EOF && Peek() != c)
                Move();
            return socHtml.Substring(start, this.index - start);
        }

        private string ParseAttributeValue2()
        {
            int start = this.index;
            while (!EOF && !Char.IsWhiteSpace(Peek()))
                Move();
            return socHtml.Substring(start, this.index - start);
        }

        private void ProcessWhiteSpace()
        {
            while (!EOF && Char.IsWhiteSpace(Peek()))
            {
                Move();
            }
        }

        private string ParseCData()
        {
            int start = this.index;

            while (!EOF && Peek() != '[')
            {
                Move();
            }
            return socHtml.Substring(start, this.index - start);
        }

        private string ParseCDataValue()
        {
            int start = this.index;

            while (!EOF && !(Peek() == ']' && Peek(1) == ']' && Peek(2) == '>'))
            {
                Move();
            }
            return socHtml.Substring(start, this.index - start);
        }

        private bool EOF
        {
            get { return (index >= chars.Length); }
        }

        private char Peek()
        {
            return Peek(0);
        }

        private char Peek(int ahead)
        {
            int pos = (index + ahead);
            if (pos < chars.Length)
                return chars[pos];
            return (char)0;
        }

        private void Move()
        {
            Move(1);
        }

        private void Move(int ahead)
        {
            index = Math.Min(index + ahead, chars.Length);
        }

        private char GetNextChar()
        {
            Move();
            return Peek(0);
        }
        #endregion
    }
}
